﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Xml;
using YhdSdk.Parser;
using YhdSdk.Request;
using YhdSdk.Domain;
using System.Text;
using System.Diagnostics;
using Jayrock.Json.Conversion;


namespace YhdSdk
{
    /// <summary>
    /// 基于REST的Yhd客户端。
    /// </summary>
    public class DefaultYhdClient : IYhdClient
    {
        /// <summary>
        /// 商家验证码
        /// </summary>
        public const string CHECKCODE = "checkCode";

        /// <summary>
        /// 商家ID
        /// </summary>
        public const string MERCHANTID = "merchantId";

        /// <summary>
        /// 返回数据格式
        /// </summary>
        public const string FORMAT = "format";

        /// <summary>
        /// 对接erp
        /// </summary>
        public const string ERP = "erp";

        /// <summary>
        /// 对接erp版本
        /// </summary>
        public const string ERPVER = "erpVer";

        /// <summary>
        /// 接口名称
        /// </summary>
        public const string METHOD = "method";

        /// <summary>
        /// 时间戳，没使用
        /// </summary>
        public const string TIMESTAMP = "timestamp";

        /// <summary>
        /// 版本
        /// </summary>
        public const string VERSION = "ver";

        /// <summary>
        /// 验证码
        /// </summary>
        public const string SIGN = "sign";

        /// <summary>
        /// xml format
        /// </summary>
        public const string FORMAT_XML = "xml";

        /// <summary>
        /// json format
        /// </summary>
        public const string FORMAT_JSON = "json";

        private string serverUrl;
        private string format = FORMAT_XML;
        private string erp;
        private string erpVer;
        private string checkCode;
        private string secretKey;
        private Int64 merchantId;
        private string ver = "1.0";

        private WebUtils webUtils;
        private IYhdLogger topLogger;

        private bool disableParser = false; // 禁用响应结果解释
        private bool disableTrace = true; // 禁用日志调试功能

        #region DefaultYhdClient Constructors

        public DefaultYhdClient(string serverUrl, Int64 merchantId, String secretKey, String checkCode) :
            this(serverUrl, merchantId, secretKey, checkCode, "self", "1.0")
        {
        }

        public DefaultYhdClient(string serverUrl, Int64 merchantId, String secretKey, String checkCode, String erp, String erpVer)
        {
            this.serverUrl = serverUrl;
            this.merchantId = merchantId;
            this.checkCode = checkCode;
            this.erp = erp;
            this.erpVer = erpVer;
            this.secretKey = secretKey;
            this.webUtils = new WebUtils();
            this.topLogger = new DefaultYhdLogger();
        }

        #endregion

        public void SetTimeout(int timeout)
        {
            webUtils.Timeout = timeout;
        }

        public void SetDisableParser(bool disableParser)
        {
            this.disableParser = disableParser;
        }

        public void SetDisableTrace(bool disableTrace)
        {
            this.disableTrace = disableTrace;
        }

        public void SetYhdLogger(IYhdLogger topLogger)
        {
            this.topLogger = topLogger;
        }

        #region IYhdClient Members

        public T Execute<T>(IYhdRequest<T> request) where T : YhdResponse
        {
            if (disableTrace)
            {
                return DoExecute<T>(request);
            }
            else
            {
                try
                {
                    return DoExecute<T>(request);
                }
                catch (Exception e)
                {
                    topLogger.Error(this.serverUrl + "\r\n" + e.StackTrace);
                    throw e;
                }
            }
        }

        #endregion

        private T DoExecute<T>(IYhdRequest<T> request) where T : YhdResponse
        {
            // 提前检查业务参数
            try
            {
                request.Validate();
            }
            catch (YhdException e)
            {
                return createErrorResponse<T>(e.ErrorCode, e.ErrorMsg);
            }

            // 添加协议级请求参数
            YhdDictionary txtParams = new YhdDictionary(request.GetParameters());
            
            txtParams.Add(METHOD, request.ApiName);
            txtParams.Add(VERSION, ver);
            txtParams.Add(CHECKCODE, checkCode);
            txtParams.Add(FORMAT, format);
            txtParams.Add(MERCHANTID, merchantId);
            txtParams.Add(ERP, erp);
            txtParams.Add(ERPVER, erpVer);
            txtParams.Add(SIGN, YhdUtils.SignYhdRequest(txtParams, secretKey));
            // 添加签名参数

            // 是否需要上传文件
            string body;
            if (request is IYhdUploadRequest<T>)
            {
                IYhdUploadRequest<T> uRequest = (IYhdUploadRequest<T>)request;
                IDictionary<string, FileItem> fileParams = YhdUtils.CleanupDictionary(uRequest.GetFileParameters());
                body = webUtils.DoPost(this.serverUrl, txtParams, fileParams);
            }
            else
            {
                body = webUtils.DoPost(this.serverUrl, txtParams);
            }

            // 解释响应结果
            T rsp;
            if (disableParser)
            {
                rsp = Activator.CreateInstance<T>();
                rsp.Body = body;
            }
            else
            {
                if (FORMAT_JSON.Equals(format))
                {
                    IYhdParser<T> tp = new YhdJsonParser<T>();
                    rsp = tp.Parse(body);
                }
                else
                {
                    IYhdParser<T> tp = new YhdXmlParser<T>();
                    rsp = tp.Parse(body);
                }
            }

            // 追踪错误的请求
            if (!disableTrace)
            {
                rsp.ReqUrl = webUtils.BuildGetUrl(this.serverUrl, txtParams);
                if (rsp.IsError)
                {
                    topLogger.Warn(rsp.ReqUrl + "\r\n" + rsp.Body);
                }
            }

            return rsp;
        }

        private T createErrorResponse<T>(string errCode, string errMsg) where T : YhdResponse
        {
            T rsp = Activator.CreateInstance<T>();
            ErrDetailInfo err = new ErrDetailInfo();
            err.ErrorCode = errCode;
            err.ErrorDes = errMsg;
            rsp.ErrInfoList = new List<ErrDetailInfo>();
            rsp.ErrInfoList.Add(err);

            if (FORMAT_XML.Equals(format))
            {
                XmlDocument root = new XmlDocument();
                XmlElement bodyE = root.CreateElement("error_response");
                XmlElement codeE = root.CreateElement("code");
                codeE.InnerText = errCode;
                bodyE.AppendChild(codeE);
                XmlElement msgE = root.CreateElement("msg");
                msgE.InnerText = errMsg;
                bodyE.AppendChild(msgE);
                root.AppendChild(bodyE);
                rsp.Body = root.OuterXml;
            }
            else
            {
                IDictionary<string, object> errObj = new Dictionary<string, object>();
                errObj.Add("code", errCode);
                errObj.Add("msg", errMsg);
                IDictionary<string, object> root = new Dictionary<string, object>();
                root.Add("error_response", errObj);

                string body = JsonConvert.ExportToString(root);
                rsp.Body = body;
            }

            return rsp;
        }
    }
}
